using LinearAlgebra, LinearAlgebra.LAPACK

function make_tuple(x)
    return isa(x, Union{Tuple, Array}) ? x : (x,)
end

function demean(x)
    return x .- sum(x)/length(x)
end

# LU Factorization
function factor(X)
    return factorize(X)
end

function factored_solve(Z, y)
    return Z \ y
end

function unprime(s)
    if (s[end-1:end] == "_p")
        return s[1:end-2]
    else
        return s
    end
end

function uncapitalize(s)
    return lowercasefirst(s)
end

function list_diff(l1, l2)
   return setdiff(l1, l2)
end

function dict_diff(d1, d2)
    dict = Dict()
    for k in setdiff(keys(d1), keys(d2))
        dict[k] = d1[k]
    end
    return dict
end

function smart_set(data)
    if (isa(data, AbstractString))
        return Set([data])
    else
        return Set(data)
    end
end

function smart_zip(keys, values)
    if isa(values, Float64)
        return zip(keys, [values])
    else
        return zip(keys, values)
    end
end

function smart_zeros(n)
    if n > 1
        return zeros(n)
    else
        return 0.0
    end
end

function logit(V, scale)
    Vmax = maximum(V, dims=1)
    Vnorm = V .- Vmax
    Vexp = exp.(Vnorm / scale)
    P = Vexp ./ sum(Vexp, dims=1)
    return P
end

function logsum(V, scale)
    Vmax = maximum(V, dims=1)
    Vnorm = V .- Vmax  #normalises V
    Vexp = exp.(Vnorm / scale)
    Vsum = sum(Vexp, dims=1)
    EV = Vmax + scale .* log.(Vsum)
    return EV
end

function logit_choice(V, scale)
    P = logit(V, scale)
    EV = logsum(V, scale)
    return P, EV
end

@generated function nonconcave(Va, ilower, iupper)
    nA = size(Va)
    vmin = Inf
    vmax = -Inf
    # find vmin & vmax
    for ia in 1:(nA-1)
        if Va[ia+1] > Va[ia]
            vmin_temp = Va[ia]
            vmax_temp = Va[ia+1]
            if vmin_temp < vmin
                vmin = vmin_temp
            end
            if vmax_temp > vmax
                vmax = vmax_temp
            end
        end
    end
    # Find ilower
    if vmax == -Inf
        ilower_ = nA
    else
        ia = nA
        while ia > 0
            if Va[ia] > vmax
                break
            end
            ia -= 1
        end
        ilower_ = ia
    end

    # Find iupper
    if vmin == Inf
        iupper_ = 0
    else
        ia = 0
        while ia < nA
            if Va[ia] < vmin
                break
            end
            ia += 1
        end
        iupper_ = ia
    end
end
